sequenceDiagram
autonumber
participant Core as Метод 3 (CoreSimulationEngineImpl)
participant G as ReceiptPayloadGenerator
participant Catalog as Реестр Каталога (20к SKU)
participant Q as Метод 10 (WeightQuantizer)
%% ВХОД МЕТОДА
Core->>G: Вызов generateReceiptPayload(TwinContext)
activate G
Note over G: Вход метода: TwinContext с личными весами предпочтений K пользователя
G->>G: Сборка заголовка чека и токена карты лояльности
G->>G: Определение размера корзины: N = random(1, 5)
%% СТОХАСТИЧЕСКИЙ ЦИКЛ НАПОЛНЕНИЯ КОРЗИНЫ
loop Повторение для каждой из N позиций чека
G->>Catalog: Запрос случайного SKU на основе весов предпочтений
activate Catalog
Note over Catalog: Алгоритм отсекает экзотику, выбирая товар<br/>из ядра 250-300 привычных продуктов Твина
Catalog-->>G: Возврат: SKU Карточка (Артикул, Цена, Категория)
deactivate Catalog
G->>Q: Вызов calculateStochasticQuantities(sku)
activate Q
Q-->>G: Возврат: Квантованный объем BigDecimal (Half-Even до 4 знаков)
deactivate Q
G->>G: Расчет стоимости позиции: Цена умножить на Объем
end
G->>G: Математическое суммирование всех позиций в общее поле чека
Note over G: Выход метода: Сформированный и готовый к сетевой отправке DTO чека покупки
G-->>Core: Возврат: Фискальный пакет ReceiptWebhookPayload DTO
deactivate G
generate_receipt_payload()
Домен: SIMULATION | Контур: Сборка фискального чека покупки
В открытом доступе представлена демонстрационная версия метода. В настоящей публичной документации отображены не все шаги, технические сценарии и приватные эндпоинты для системы цифровых симуляторов бизнес-процессов.
- Полная спецификация метода: Будет доступна только во внутреннем контуре разработки (Confluence / Swagger Enterprise).
1. Бизнес-спецификация метода
- Идентификатор метода:
BPDS-SIM-M011 - Системное имя:
generate_receipt_payload(context: TwinContext): ReceiptWebhookPayload - Микросервис:
simulation-core-engine - Домен:
SIMULATION - Класс / Компонент:
simulation.engine.helpers.ReceiptPayloadGenerator
1.1. Описание логики работы
Этот метод выполняет роль сборщика корзины покупок, когда на Шаге 9 рулетка определила сценарий Покупки продукты (SCENARIO_B2B_BUY_RECEIPT). Метод имитирует работу кассового аппарата магазина (например, Magnum или Small). Он определяет случайный размер корзины — сколько разных товаров пользователь купит за этот раз (от 1 до 5 позиций).
Чтобы чек выглядел жизненно, метод решает проблему «экзотических продуктов». Если выбирать товары из общего каталога сети (20 000+ позиций) вслепую, пользователь постоянно покупал бы кокосовое молоко или папайю, забывая про базовый хлеб.
Поэтому метод перемножает базовую популярность товаров в Алматы (\(W_{base}\)) на личные вкусы пользователя (\(K\)), полученные из базы данных на Шаге 4. Это автоматически сужает огромный каталог до ядра из 250–300 привычных продуктов повседневного спроса.
Для каждого выбранного товара метод вызывает Метод 10, чтобы узнать его точный вес или штуки, берет цену из справочника и рассчитывает итоговую стоимость чека в тенге (KZT).
1.2. Пошаговое выполнение
- Генерация метаданных: Метод создает уникальный номер кассовой транзакции, случайный фискальный признак чека и ставит текущее время симуляции.
- Токенизация лояльности: Идентификатор пользователя
twin_idмаппируется в токен карты лояльности магазина (например,token_almaty_loyalty_[короткий_id]_gold) для последующей идентификации на бэкенде. - Определение размера корзины: Задается случайное число позиций в чеке \(N\) (от 1 до 5 разных товаров).
- Сужение каталога: Симулятор берет 20 000+ позиций розничной сети и через перемножение весов (\(W_{final} = W_{base} \times K\)) оставляет только ядро из 250–300 привычных продуктов конкретного пользователя.
- Наполнение чека: Для каждого из \(N\) товаров:
- Из памяти извлекается SKU, макро-категория и цена в тенге.
- Вызывается Метод 10 (
calculate_stochastic_quantities) для генерации веса (Half-Even до 4 знаков). - Цена умножается на полученный вес, округляя стоимость этой позиции до 2 знаков (типа
HALF_UP).
- Подсчет общего итога: Стоимость всех позиций суммируется в итоговую сумму
total_amount_kzt, и сформированный чек передается дальше сетевому контуру отправки (Метод 15).
2. Диаграмма последовательности метода (Вход и Выход флоу)
Диаграмма наглядно показывает, как метод принимает контекст пользователя, сужает каталог розницы Алматы до ядра привычных продуктов, рассчитывает стоимости через квантователь веса и выдает готовый payload фискального чека.
3. Схемы данных и SQL-взаимодействие
Этот метод формирует кассовые транзакции «черного ящика» внутри оперативной памяти симулятора и напрямую запросы к PostgreSQL симулятора на этом этапе не делает. Данные каталога на 20 000+ SKU (цены, макро-категории и их базовая популярность в Алматы) хранятся в резидентном кэше памяти.
4. Спецификация обмена данными (Вход / Выход)
Данные чека подготавливаются для последующей передачи в метод отправки сетевого пайплайна.
4.1. Входной контекст Твина (Входные параметры для метода)
{
"twin_id": "d3b07384-d113-4956-bf8a-e421cd7bf777",
"current_tick": 105,
"preference_vector_k": {
"FRUITS": 1.4500,
"DAIRY": 0.9000
}
}4.2. Сгенерированная структура фискального чека (Выходные параметры для Метода 15)
Чек наполнен базовыми привычными товарами Твина (молоко «Амал» и бананы), полностью исключив закуп спаржи или экзотических фруктов:
{
"transaction_id": "tx-20260810-994103",
"loyalty_card_token": "token_almaty_loyalty_d3b07384_gold",
"store_id": "store_almaty_alatau_01",
"fiscal_sign": "KZ_FISC_993184102",
"purchased_at": "2026-08-11T09:47:00Z",
"items": [
{
"sku": "BANANA_ECUADOR_KG",
"product_name": "БАНАН ЭКВАДОР КГ",
"category_id": "FRUITS",
"quantity": 1.6950,
"unit": "kg",
"price_per_unit_kzt": 850.00,
"total_item_amount_kzt": 1440.75
},
{
"sku": "MILK_AMAL_1L_3.2",
"product_name": "Молоко Амал Свежее 3.2% 1л",
"category_id": "DAIRY",
"quantity": 2.0000,
"unit": "pcs",
"price_per_unit_kzt": 520.00,
"total_item_amount_kzt": 1040.00
}
],
"total_amount_kzt": 2480.75
}5. ЗАДАЧА ДЛЯ РАЗРАБОТЧИКА: BACKEND
Заголовок: Реализация хелпера стохастического формирования фискального JSON чека generate_receipt_payload с защитой от экзотических SKU
5.1. Что нужно сделать
- Написать Java-класс
ReceiptPayloadGeneratorв пакетеsimulation.engine.helpersи пометить его аннотацией@Component. Внедрить зависимостьWeightQuantizer. - Создать легковесный внутренний класс
AlmatyCatalogRegistry, имитирующий справочник цен розничной сети на 20 000+ SKU. Каждому SKU сопоставить его базовую региональную популярностьbaseWeight(у молока/бананов —0.85, у экзотики —0.0001). - Написать метод
generateReceiptPayload. Размер корзины определять динамически черезThreadLocalRandom.current().nextInt(1, 6). - Реализовать стохастический отбор SKU: перемножать
baseWeightтовара на личный коэффициент предпочтения Твина изcontext.preferenceVectorK(), сужая выборку до ядра из 250–300 привычных продуктов. - Для каждого выбранного SKU вызвать
weightQuantizer.calculateStochasticQuantities(sku). Сделать расчет стоимости позиции: умножить базовую цену на квантованный объем, округлив значение до 2 знаков после запятой по стандартуRoundingMode.HALF_UP. - Просуммировать стоимости всех позиций в общее поле чека
totalAmount, упаковать в иммутабельный рекордReceiptWebhookPayloadи вернуть его оркестратору.
6. ЗАДАЧА ДЛЯ РАЗРАБОТЧИКА: МИГРАЦИЯ
Заголовок: Приведение финансовых типов полей фискальных транзакций касс к точности NUMERIC(12, 2) на бэкенде приложения
6.1. Что нужно сделать
Чтобы отправляемые симулятором финансовые агрегаты чеков (total_amount_kzt) и стоимости позиций успешно парсились бэкендом приложения розничной сети Алматы без потери центов и погрешностей округлений, необходимо зафиксировать ограничения схемы public в PostgreSQL бэкенда.
-- Изменения для продуктовой базы данных бэкенда розничной сети (схема public)
ALTER TABLE public.store_receipts
ALTER COLUMN total_amount_kzt TYPE NUMERIC(12, 2);
-- Защитный барьер: чек из кассового шлюза не может иметь нулевую или отрицательную стоимость
ALTER TABLE public.store_receipts
ADD CONSTRAINT chk_min_receipt_amount CHECK (total_amount_kzt > 0.00);